/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/Module_interface.h"
#include "simodo/variable/VariableSetWrapper.h"
#include "simodo/interpret/Interpret_interface.h"

#include <memory>
#include <exception>
#include <cassert>

#ifdef CROSS_WIN
// MinGW related workaround
#define BOOST_DLL_FORCE_ALIAS_INSTANTIATION
#endif

#include <boost/dll/alias.hpp>

using namespace simodo;
using namespace simodo::variable;

namespace
{
    Value createInt(Module_interface * host, const VariableSetWrapper & args);
    Value createIntOne(Module_interface * host, const VariableSetWrapper & args);
    Value createAny(Module_interface * host, const VariableSetWrapper & args);
}

class Array_module : public Module_interface
{
public:
    virtual version_t version() const override { return lib_version(); }

    virtual Object instantiate(std::shared_ptr<Module_interface> module_object) override 
    {
        return {{
            {u"int", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, ::createInt}},
                {{}, ValueType::Array},
                {u"size", ValueType::Int},
            }}}},
            {u"int1", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, ::createIntOne}},
                {{}, ValueType::Array},
            }}}},
            {u"any", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, ::createAny}},
                {{}, ValueType::Array},
                {u"size", ValueType::Int},
                {u"type", ValueType::Null},
            }}}},
        }};
    }

    // Factory method
    static std::shared_ptr<Module_interface> create(interpret::Interpret_interface * ) {
        return std::make_shared<Array_module>();
    }
};

BOOST_DLL_ALIAS(
    Array_module::create,    // <-- this function is exported with...
    create_simodo_module        // <-- ...this alias name
)

namespace
{
    Value createInt(Module_interface * host, const VariableSetWrapper & args)
    {
        if (host == nullptr)
            return {};

        assert(args.size() == 1);

        int64_t size = args[0].origin().value().getInt();

        if (size <= 0 || size > 32654)
            throw std::invalid_argument("Invalid argument");

        std::vector<Value> array(size, 0);

        return array;
    }

    Value createIntOne(Module_interface * host, const VariableSetWrapper & args)
    {
        if (host == nullptr)
            return {};

        assert(args.size() == 0);

        std::vector<Value> array {{0}};

        return array;
    }

    Value createAny(Module_interface * host, const VariableSetWrapper & args)
    {
        if (host == nullptr)
            return {};

        assert(args.size() == 2);

        int64_t size       = args[0].origin().value().getInt();
        const Value & type = args[1].origin().value();

        if (size <= 0 || size > 32654)
            throw std::invalid_argument("Invalid argument");

        std::vector<Value> array;

        array.reserve(size);

        for(int64_t i=0; i < size; ++i)
            array.push_back(type.copy());

        // std::cerr << "*** module.init out" << std::endl;

        return array;
    }

}

